#ifndef STX_ARRAY_HPP
#define STX_ARRAY_HPP

#include <stx/core.hpp>
#include <stx/uninitialized.hpp>
#include <cstddef>

namespace stx {

template<typename T, size_t max_size_>
class array
{
public:
    
    typedef array<T, max_size_> this_type;
    typedef T value_type;
    typedef T* pointer;
    typedef const T* const_pointer;
    typedef T& reference;
    typedef const T& const_reference;
    typedef std::size_t size_type;
    typedef std::ptrdiff_t difference_type;
    
    typedef pointer iterator;
    typedef const_pointer const_iterator;
    
    array(size_type size_ = 0)
    {
        #if (STX_DEBUG)
        if (size_ > capacity()) {
            STX_THROW(std::runtime_error, "array(size_type) failed; size must be <= capacity()"
                "; size: " << size_ << "; capacity(): " << capacity());
        }
        #endif
        end_ = begin() + size_;
        if (!is_fundamental<value_type>::value) {
            for (size_type i = 0; i < size(); ++i) {
                buf_[i].construct();
            }
        }
    }
    
    ~array()
    {
        clear();
    }
    
    reference at(size_type n)
    {
        return buf_[n].get();
    }
    
    const_reference at(size_type n) const
    {
        return buf_[n].get();
    }
    
    reference front()
    {
        return at(0);
    }
    
    const_reference front() const
    {
        return at(0);
    }
    
    reference back()
    {
        return at(size() - 1);
    }
    
    const_reference back() const
    {
        return at(size() - 1);
    }
    
    iterator begin()
    {
        return buf_->get_ptr();
    }
    
    const_iterator begin() const
    {
        return buf_->get_ptr();
    }
    
    iterator end()
    {
        return end_;
    }
    
    const_iterator end() const
    {
        return end_;
    }
    
    size_type capacity() const
    {
        return max_size_;
    }
    
    void clear()
    {
        resize(0);
    }
    
    bool empty()
    {
        return buf_ == end_;
    }
    
    size_type max_size() const
    {
        return max_size_;
    }
    
    void push_back(const T& x)
    {
        at(size()) = x;
        end_ += 1;
    }
    
    void pop_back()
    {
        if (!is_fundamental<value_type>::value) {
            back().destruct();
        }
        end_ -= 1;
    }
    
    void resize(size_type size_)
    {
        if (size_ < size()) {
            if (!is_fundamental<value_type>::value) {
                for (size_type i = 0; i < size(); ++i) {
                    buf_[i].destruct();
                }
            }
        } else {
            if (!is_fundamental<value_type>::value) {
                value_type default_value = value_type();
                for (size_type i = 0; i < size(); ++i) {
                    buf_[i].get() = default_value;
                }
            }
        }
        end_ = begin() + size_;
    }
    
    size_type size() const
    {
        return end() - begin();
    }
    
    reference operator[] (size_type n)
    {
        return at(n);
    }
    
    const_reference operator[] (size_type n) const
    {
        return at(n);
    }
    
private:
    
    uninitialized<T> buf_[max_size_];
    
    pointer end_;
};

} // namespace stx

#endif // STX_ARRAY_HPP
